{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bc5f4dcb",
   "metadata": {},
   "source": [
    "# 第八讲 列表（二）\n",
    "\n",
    "## 1. 管理列表\n",
    "\n",
    "在你创建的列表中，元素的排列顺序常常是无法预测的，因为你并非总能控制用户提供数据的顺序。这虽然在大多数情况下是不可避免的，但你经常需要以特定的顺序呈现信息。你有时候希望保留列表元素最初的排列顺序，而有时候又需要调整排列顺序。Python 提供了很多管理列表的方式，可根据具体情况选用。\n",
    "\n",
    "## 2. 使用 sort() 方法对列表进行永久排序\n",
    "Python 方法 `sort()` 让你能够较为轻松地对列表进行排序。假设你有一个汽车列表，并要让其中的汽车按字母顺序排列。为了简化这项任务，假设该列表中的所有值都是全小写的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6cc71046",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['audi', 'bmw', 'subaru', 'toyota']\n"
     ]
    }
   ],
   "source": [
    "# cars.py\n",
    "cars = ['bmw', 'audi', 'toyota', 'subaru']\n",
    "cars.sort()\n",
    "print(cars)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a6002ea",
   "metadata": {},
   "source": [
    "#### 重要特点：\n",
    "\n",
    "- `sort()` 方法能**永久地**修改列表元素的排列顺序\n",
    "- 现在，汽车是按字母顺序排列的，再也无法恢复到原来的排列顺序"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3358529",
   "metadata": {},
   "source": [
    "## 3. 反向排序\n",
    "\n",
    "还可以按与字母顺序相反的顺序排列列表元素，只需向 `sort()` 方法传递参数 `reverse=True` 即可。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ffd8dd72",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['toyota', 'subaru', 'bmw', 'audi']\n"
     ]
    }
   ],
   "source": [
    "cars = ['bmw', 'audi', 'toyota', 'subaru']\n",
    "cars.sort(reverse=True)\n",
    "print(cars)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ddf2b1ad",
   "metadata": {},
   "source": [
    "## 4. 使用 sorted() 函数对列表进行临时排序\n",
    "要保留列表元素原来的排列顺序，并以特定的顺序呈现它们，可使用 `sorted()` 函数。`sorted()` 函数让你能够按特定顺序显示列表元素，同时不影响它们在列表中的排列顺序。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "dec39fca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Here is the original list:\n",
      "['bmw', 'audi', 'toyota', 'subaru']\n",
      "\n",
      "Here is the sorted list:\n",
      "['audi', 'bmw', 'subaru', 'toyota']\n",
      "\n",
      "Here is the original list again:\n",
      "['bmw', 'audi', 'toyota', 'subaru']\n"
     ]
    }
   ],
   "source": [
    "cars = ['bmw', 'audi', 'toyota', 'subaru']\n",
    "\n",
    "print(\"Here is the original list:\")\n",
    "print(cars)\n",
    "\n",
    "print(\"\\nHere is the sorted list:\")\n",
    "print(sorted(cars))\n",
    "\n",
    "print(\"\\nHere is the original list again:\")\n",
    "print(cars)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "729d5761",
   "metadata": {},
   "source": [
    "#### 重要特点：\n",
    "\n",
    "1. 首先按原始顺序打印列表\n",
    "2. 再按字母顺序显示该列表\n",
    "3. 以特定顺序显示列表后进行核实，确认列表元素的排列顺序与以前相同\n",
    "\n",
    "#### 注意事项：\n",
    "\n",
    "- 在调用 `sorted()` 函数后，列表元素的排列顺序并没有变化\n",
    "- 如果要按与字母顺序相反的顺序显示列表，也可向 `sorted()` 函数传递参数 `reverse=True`\n",
    "\n",
    "**重要提醒：** 在并非所有的值都是全小写的时，按字母顺序排列列表要复杂一些。在确定排列顺序时，有多种解读大写字母的方式，此时要指定准确的排列顺序，可能会比这里所做的更加复杂。然而，大多数排序方式是以本节介绍的知识为基础的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2007660c",
   "metadata": {},
   "source": [
    "## 5. 反向打印列表\n",
    "要反转列表元素的排列顺序，可使用 `reverse()` 方法。假设汽车列表是按购买时间排列的，可轻松地按相反的顺序排列其中的汽车："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a58043c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['bmw', 'audi', 'toyota', 'subaru']\n",
      "['subaru', 'toyota', 'audi', 'bmw']\n"
     ]
    }
   ],
   "source": [
    "cars = ['bmw', 'audi', 'toyota', 'subaru']\n",
    "print(cars)\n",
    "\n",
    "cars.reverse()\n",
    "print(cars)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88b477d3",
   "metadata": {},
   "source": [
    "#### 重要特点：\n",
    "\n",
    "- `reverse()` **不是**按与字母顺序相反的顺序排列列表元素\n",
    "- 只是反转列表元素的排列顺序\n",
    "- `reverse()` 方法会**永久地**修改列表元素的排列顺序\n",
    "- 但可随时恢复到原来的排列顺序，只需对列表再次调用 `reverse()` 即可"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "691839e6",
   "metadata": {},
   "source": [
    "## 6. 确定列表的长度\n",
    "使用 `len()` 函数可快速获悉列表的长度。在下面的示例中，列表包含 4 个元素，因此其长度为 4："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d507a0c3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n"
     ]
    }
   ],
   "source": [
    "cars = ['bmw', 'audi', 'toyota', 'subaru']\n",
    "print(len(cars))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "117d343e",
   "metadata": {},
   "source": [
    "#### 实际应用场景：\n",
    "\n",
    "在需要完成如下任务时，`len()` 很有用：\n",
    "\n",
    "- 明确还有多少个外星人未被消灭\n",
    "- 确定需要管理多少项可视化数据\n",
    "- 计算网站有多少注册用户\n",
    "- 等等\n",
    "\n",
    "**重要提醒：** Python 在计算列表元素数时从 1 开始，因此你在确定列表长度时应该不会遇到差一错误。\n",
    "\n",
    "#### 总结对比\n",
    "\n",
    "| 方法/函数   | 是否永久修改 | 用途     | 示例             |\n",
    "| ----------- | ------------ | -------- | ---------------- |\n",
    "| `sort()`    | 是           | 永久排序 | `cars.sort()`    |\n",
    "| `sorted()`  | 否           | 临时排序 | `sorted(cars)`   |\n",
    "| `reverse()` | 是           | 反转顺序 | `cars.reverse()` |\n",
    "| `len()`     | 否           | 获取长度 | `len(cars)`      |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58c99952",
   "metadata": {},
   "source": [
    "## 7. 使用列表时避免索引错误\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "219e03ab",
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "list index out of range",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[1;32mIn [1], line 3\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# motorcycles.py\u001b[39;00m\n\u001b[0;32m      2\u001b[0m motorcycles \u001b[38;5;241m=\u001b[39m [\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhonda\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124myamaha\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124msuzuki\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mmotorcycles\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m]\u001b[49m)\n",
      "\u001b[1;31mIndexError\u001b[0m: list index out of range"
     ]
    }
   ],
   "source": [
    "# motorcycles.py\n",
    "motorcycles = ['honda', 'yamaha', 'suzuki']\n",
    "print(motorcycles[3])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7601ae8b",
   "metadata": {},
   "source": [
    "### 错误原因分析\n",
    "\n",
    "#### 索引从 0 开始的特征\n",
    "\n",
    "- Python 试图提供位于索引 3 处的元素\n",
    "- 但当它搜索列表 `motorcycles` 时，却发现索引 3 处没有元素\n",
    "- **鉴于列表索引差一的特征，这种错误很常见**\n",
    "\n",
    "#### 常见误解\n",
    "\n",
    "- 有些人从 1 开始数，因此以为第三个元素的索引为 3\n",
    "- 但是在 Python 中，**第三个元素的索引为 2，因为索引是从 0 开始的**\n",
    "\n",
    "| 元素值 | 'honda' | 'yamaha' | 'suzuki' |\n",
    "| ------ | ------- | -------- | -------- |\n",
    "| 索引   | 0       | 1        | 2        |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72d5caa9",
   "metadata": {},
   "source": [
    "### 7.1 解决索引错误的方法\n",
    "\n",
    "#### 方法一：调整索引值\n",
    "\n",
    "**索引错误意味着 Python 在指定索引处找不到元素。**\n",
    "\n",
    "**解决步骤：**\n",
    "\n",
    "1. 在程序发生索引错误时，请尝试将指定的索引减 1\n",
    "2. 然后再次运行程序，看看结果是否正确\n",
    "\n",
    "#### 方法二：使用负索引访问最后一个元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "25d22f3b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "suzuki\n"
     ]
    }
   ],
   "source": [
    "motorcycles = ['honda', 'yamaha', 'suzuki']\n",
    "print(motorcycles[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d6c2e56",
   "metadata": {},
   "source": [
    "#### 负索引的优势\n",
    "\n",
    "- **索引 -1 总是返回最后一个列表元素**\n",
    "- 这在任何情况下都行之有效\n",
    "- 即便在你最后一次访问列表后，其长度发生了变化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5754c204",
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "list index out of range",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[1;32mIn [3], line 4\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m#  空列表访问最后一个元素\u001b[39;00m\n\u001b[0;32m      3\u001b[0m motorcycles \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m----> 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mmotorcycles\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m)\n",
      "\u001b[1;31mIndexError\u001b[0m: list index out of range"
     ]
    }
   ],
   "source": [
    "#  空列表访问最后一个元素\n",
    "\n",
    "motorcycles = []\n",
    "print(motorcycles[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21223901",
   "metadata": {},
   "source": [
    "#### 原因分析\n",
    "\n",
    "- 列表 `motorcycles` 不包含任何元素\n",
    "- 因此 Python 返回一条索引错误消息\n",
    "- **仅当列表为空时，这种访问最后一个元素的方式才会导致错误**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "97caed15",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "列表内容: []\n",
      "列表长度: 0\n",
      "列表为空\n"
     ]
    }
   ],
   "source": [
    "# 在发生索引错误却找不到解决办法时，请尝试将列表或其长度打印出来。\n",
    "\n",
    "# 打印列表内容\n",
    "print(\"列表内容:\", motorcycles)\n",
    "# 打印列表长度\n",
    "print(\"列表长度:\", len(motorcycles))\n",
    "# 然后再访问元素\n",
    "if len(motorcycles) > 0:\n",
    "    print(\"最后一个元素:\", motorcycles[-1])\n",
    "else:\n",
    "    print(\"列表为空\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "daa4e005",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "列表为空，无法访问元素\n"
     ]
    }
   ],
   "source": [
    "# 安全的列表访问方式\n",
    "if motorcycles:  # 检查列表是否非空\n",
    "    print(\"第一个元素:\", motorcycles[0])\n",
    "    print(\"最后一个元素:\", motorcycles[-1])\n",
    "    print(\"列表长度:\", len(motorcycles))\n",
    "else:\n",
    "    print(\"列表为空，无法访问元素\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "RobinLaw",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
